package com.yupi.springbootinit.service.impl;

import static com.yupi.springbootinit.constant.UserConstant.USER_LOGIN_STATE;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.mapper.UserMapper;
import com.yupi.springbootinit.model.dto.user.UserQueryRequest;
import com.yupi.springbootinit.model.dto.user.UserUpdateMyPasswordRequest;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.enums.UserRoleEnum;
import com.yupi.springbootinit.model.vo.LoginUserVO;
import com.yupi.springbootinit.model.vo.UserVO;
import com.yupi.springbootinit.model.vo.UserStatisticsVO;
import com.yupi.springbootinit.model.vo.DepartmentUserStatVO;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.mapper.DepartmentMapper;
import com.yupi.springbootinit.model.entity.Department;
import com.yupi.springbootinit.service.LoginLogService;
import com.yupi.springbootinit.utils.NetUtils;
import com.yupi.springbootinit.utils.SqlUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

/**
 * 用户服务实现
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private LoginLogService loginLogService;

    @Resource
    private DepartmentMapper departmentMapper;

    /**
     * 盐值，混淆密码
     */
    public static final String SALT = "yupi";

    @Override
    public long userRegister(String userAccount, String userName, String userPassword, String checkPassword, Long departmentId) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userName, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        //用户名不能重复，且只能为中文
        if (!userName.matches("^[\\u4e00-\\u9fa5]+$")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名格式有误");
        }
        //用户名不能重复
        QueryWrapper<User> queryWrapperName = new QueryWrapper<>();
        queryWrapperName.eq("userName", userName);
        if (this.baseMapper.selectOne(queryWrapperName) != null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名重复");
        }
        //密码不能少于8位
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        synchronized (userAccount.intern()) {
            // 账户不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userAccount", userAccount);
            long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            // 3. 插入数据
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserName(userName);
            user.setUserPassword(encryptPassword);
            user.setDepartmentId(departmentId);
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }
            return user.getId();
        }
    }

    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        // 1. 校验参数
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4 || userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号或密码格式不正确");
        }

        // 2. 查询用户
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        User user = this.baseMapper.selectOne(queryWrapper);

        // 3. 用户不存在：直接返回错误，不记录失败次数
        if (user == null) {
            log.info("User not found: {}", userAccount);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户或密码错误！");
        }

        // 4. 优先检查用户角色是否为ban
        if (UserRoleEnum.BAN.getValue().equals(user.getUserRole())) {
            log.warn("Banned user attempted to login: {}", userAccount);
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "该账户已被封锁！");
        }

        // 5. 验证密码
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        if (!user.getUserPassword().equals(encryptPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户或密码错误！");
        }

        // 8. 登录成功逻辑
        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        String ipAddress = NetUtils.getIpAddress(request);
        loginLogService.saveLoginLog(userAccount, user.getUserName(), ipAddress);
        return this.getLoginUserVO(user);
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        currentUser = this.getById(userId);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }

    /**
     * 获取当前登录用户（允许未登录）
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUserPermitNull(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            return null;
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        return this.getById(userId);
    }

    /**
     * 是否为管理员
     *
     * @param request
     * @return
     */
    @Override
    public boolean isAdmin(HttpServletRequest request) {
        // 仅管理员可查询
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User user = (User) userObj;
        return isAdmin(user);
    }

    /**
     * 是否为管理员
     * @param user
     * @return
     */
    @Override
    public boolean isAdmin(User user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        // 移除登录态
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return true;
    }

    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setUserName(user.getUserName());
        BeanUtils.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public List<UserVO> getUserVO(List<User> userList) {
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    /**
     * 获取查询条件
     * @param userQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String unionId = userQueryRequest.getUnionId();
        String mpOpenId = userQueryRequest.getMpOpenId();
        String userName = userQueryRequest.getUserName();
        String userContact = userQueryRequest.getUserContact();
        String userRole = userQueryRequest.getUserRole();
        Long departmentId = userQueryRequest.getDepartmentId();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(id != null, "id", id);
        queryWrapper.eq(StringUtils.isNotBlank(unionId), "unionId", unionId);
        queryWrapper.eq(StringUtils.isNotBlank(mpOpenId), "mpOpenId", mpOpenId);
        queryWrapper.eq(StringUtils.isNotBlank(userRole), "userRole", userRole);
        queryWrapper.eq(departmentId != null, "departmentId", departmentId);
        queryWrapper.like(StringUtils.isNotBlank(userContact), "userContact", userContact);
        queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 管理员新增/更新用户校验逻辑
     * @param user
     * @param isCreate
     */
    @Override
    public void validateAdminUser(User user, boolean isCreate) {
        // 基础校验（公共部分）
        if (user == null || StringUtils.isAnyBlank(user.getUserAccount(), user.getUserName())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户信息不完整");
        }

        // 中文校验（公共部分）
        if (!user.getUserName().matches("^[\\u4e00-\\u9fa5]+$")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名必须为中文");
        }

        if (isCreate) {
            validateForCreate(user);
        } else {
            validateForUpdate(user);
        }
    }

    /**
     * 更新用户密码
     * @param loginUser
     * @param userUpdateMyPasswordRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateMyPassword(User loginUser, UserUpdateMyPasswordRequest userUpdateMyPasswordRequest) {
        // 参数校验
      if (StringUtils.isAnyBlank(userUpdateMyPasswordRequest.getOldPassword(),
              userUpdateMyPasswordRequest.getNewPassword(),
              userUpdateMyPasswordRequest.getCheckPassword())) {
          throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不能为空");
      }
        // 验证旧密码（使用请求参数中的oldPassword）
        String encryptOldPassword = DigestUtils.md5DigestAsHex((SALT + userUpdateMyPasswordRequest.getOldPassword()).getBytes());
        if (!loginUser.getUserPassword().equals(encryptOldPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "旧密码错误");
        }

        // 更新密码
        String encryptNewPassword = DigestUtils.md5DigestAsHex((SALT + userUpdateMyPasswordRequest.getNewPassword()).getBytes());
        User updateUser = new User();
        updateUser.setId(loginUser.getId());
        updateUser.setUserPassword(encryptNewPassword);

        return this.updateById(updateUser);
    }

    // 新增用户校验（独立方法）
    private void validateForCreate(User user) {
        // 用户名重复校验
        QueryWrapper<User> nameQuery = new QueryWrapper<User>().eq("userName", user.getUserName());
        if (this.count(nameQuery) > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名已存在");
        }

        // 账号重复校验
        QueryWrapper<User> accountQuery = new QueryWrapper<User>().eq("userAccount", user.getUserAccount());
        if (this.count(accountQuery) > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");
        }
    }

    // 更新用户校验（独立方法）
    private void validateForUpdate(User user) {
        User originalUser = this.getById(user.getId());
        if (originalUser == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }

        // 用户名修改检测
        if (!originalUser.getUserName().equals(user.getUserName())) {
            QueryWrapper<User> nameQuery = new QueryWrapper<User>()
                    .eq("userName", user.getUserName())
                    .ne("id", user.getId());
            if (this.count(nameQuery) > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名已存在");
            }
        }

        // 账号修改检测
        if (!originalUser.getUserAccount().equals(user.getUserAccount())) {
            QueryWrapper<User> accountQuery = new QueryWrapper<User>()
                    .eq("userAccount", user.getUserAccount())
                    .ne("id", user.getId());
            if (this.count(accountQuery) > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");
            }
        }
    }

    /**
     * 管理员解锁用户账户（解除ban状态）
     * @param userAccount 用户账户
     * @return true - 解锁成功，false - 用户未被锁定
     */
    @Override
    public boolean unlockUser(String userAccount) {
        // 更新用户角色为user
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        User user = new User();
        user.setUserRole(UserRoleEnum.USER.getValue());
        return this.update(user, queryWrapper);
    }

    /**
     * 获取用户统计信息
     * @return 用户统计信息
     */
    @Override
    public UserStatisticsVO getUserStatistics() {
        UserStatisticsVO statistics = new UserStatisticsVO();
        
        // 统计总用户数
        QueryWrapper<User> totalQuery = new QueryWrapper<>();
        statistics.setTotalUsers(this.count(totalQuery));
        
        // 统计管理员数量
        QueryWrapper<User> adminQuery = new QueryWrapper<>();
        adminQuery.eq("userRole", UserRoleEnum.ADMIN.getValue());
        statistics.setAdminCount(this.count(adminQuery));
        
        // 统计普通用户数量
        QueryWrapper<User> userQuery = new QueryWrapper<>();
        userQuery.eq("userRole", UserRoleEnum.USER.getValue());
        statistics.setUserCount(this.count(userQuery));
        
        // 统计被封禁用户数量
        QueryWrapper<User> bannedQuery = new QueryWrapper<>();
        bannedQuery.eq("userRole", UserRoleEnum.BAN.getValue());
        statistics.setBannedCount(this.count(bannedQuery));
        
        return statistics;
    }

    /**
     * 获取各部门用户统计信息
     * @return 各部门用户统计列表
     */
    @Override
    public List<DepartmentUserStatVO> getDepartmentUserStatistics() {
        // 获取所有部门
        QueryWrapper<Department> deptQuery = new QueryWrapper<>();
        deptQuery.orderByAsc("departmentCode");
        List<Department> departments = departmentMapper.selectList(deptQuery);
        
        return departments.stream().map(department -> {
            DepartmentUserStatVO stat = new DepartmentUserStatVO();
            stat.setDepartmentId(department.getId());
            stat.setDepartmentName(department.getDepartmentName());
            stat.setDepartmentCode(department.getDepartmentCode());
            
            // 统计该部门总用户数
            QueryWrapper<User> totalQuery = new QueryWrapper<>();
            totalQuery.eq("departmentId", department.getId());
            stat.setTotalUsers(this.count(totalQuery));
            
            // 统计该部门管理员数量
            QueryWrapper<User> adminQuery = new QueryWrapper<>();
            adminQuery.eq("departmentId", department.getId())
                     .eq("userRole", UserRoleEnum.ADMIN.getValue());
            stat.setAdminCount(this.count(adminQuery));
            
            // 统计该部门普通用户数量
            QueryWrapper<User> userQuery = new QueryWrapper<>();
            userQuery.eq("departmentId", department.getId())
                    .eq("userRole", UserRoleEnum.USER.getValue());
            stat.setUserCount(this.count(userQuery));
            
            return stat;
        }).collect(Collectors.toList());
    }

    /**
     * 是否为部门负责人
     *
     * @param user 用户信息
     * @return 是否为部门负责人
     */
    @Override
    public boolean isDeptLeader(User user) {
        return user != null && UserRoleEnum.DEPT_LEADER.getValue().equals(user.getUserRole());
    }

    /**
     * 检查用户是否可以管理指定用户（部门负责人权限）
     *
     * @param manager 管理者用户
     * @param targetUserId 目标用户ID
     * @return 是否可以管理
     */
    @Override
    public boolean canManageUser(User manager, Long targetUserId) {
        if (manager == null || targetUserId == null) {
            return false;
        }
        
        // 管理员可以管理所有用户
        if (isAdmin(manager)) {
            return true;
        }
        
        // 部门负责人：只能管理同部门用户，且禁止对管理员操作
        if (isDeptLeader(manager)) {
            User targetUser = this.getById(targetUserId);
            if (targetUser == null) {
                return false;
            }
            return manager.getDepartmentId() != null
                && manager.getDepartmentId().equals(targetUser.getDepartmentId())
                && !UserRoleEnum.ADMIN.getValue().equals(targetUser.getUserRole());
        }
        
        return false;
    }

    /**
     * 获取部门负责人可管理的用户查询条件
     *
     * @param deptLeader 部门负责人
     * @param userQueryRequest 用户查询请求
     * @return 查询条件包装器
     */
    @Override
    public QueryWrapper<User> getDeptLeaderQueryWrapper(User deptLeader, UserQueryRequest userQueryRequest) {
        QueryWrapper<User> queryWrapper = getQueryWrapper(userQueryRequest);
        
        // 限制只能查询同部门的普通用户
        if (deptLeader.getDepartmentId() != null) {
            queryWrapper.eq("departmentId", deptLeader.getDepartmentId());
            queryWrapper.eq("userRole", UserRoleEnum.USER.getValue());
        } else {
            // 如果部门负责人没有部门ID，返回空结果
            queryWrapper.eq("id", -1);
        }
        
        return queryWrapper;
    }

}
